﻿#pragma once

#include "common.h"
#include <iostream>
#include <map>

namespace anip
{
	template<typename Base>
	static Base* deserialize(std::istream& in);

	template<typename Base>
	void serialize(Base& obj, std::ostream& out);

	template <typename Base>
	class Serializable
	{
		friend Base* anip::deserialize<>(std::istream& in);
		friend void anip::serialize<>(Base& obj, std::ostream& out);

	public:
		using __SerializableBase = Base;

	protected:
		class __SerializerImplementation
		{
		public:
			virtual void __serialize(Base& obj, std::ostream& out) = 0;
			virtual ~__SerializerImplementation() = default;
		};

		class __DeserializerImplementation
		{
		public:
			virtual Base* __deserialize(std::istream& in) = 0;
			virtual ~__DeserializerImplementation() = default;
		};

		virtual int __serialId() const = 0;

	private:
		typedef __DeserializerImplementation* (*DeserializerCreator)();
		typedef __SerializerImplementation* (*SerializerCreator)();
		static std::map<int, DeserializerCreator>* _deserializerCreators;
		static std::map<int, SerializerCreator>* _serializerCreators;

		static void initializeStatics()
		{
			// ensure they are initialized at this point!
			static std::map<int, DeserializerCreator> dcreators;
			static std::map<int, SerializerCreator> screators;
			_deserializerCreators = &dcreators;
			_serializerCreators = &screators;

			//cout << "size = " << dcreators.size() << endl;
		}

		template<typename T>
		static __DeserializerImplementation* _createDeserializer()
		{
			static_assert(
				std::is_base_of_v<__DeserializerImplementation, typename T::__Deserializer>,
				"Unable to find deserializer implementation");
			//static_assert(sizeof(T::__Deserializer) == 1U);
			return new typename T::__Deserializer();
		}

		template<typename T>
		static __SerializerImplementation* _createSerializer()
		{
			static_assert(
				std::is_base_of_v<__SerializerImplementation, typename T::__Serializer>,
				"Unable to find serializer implementation");
			//static_assert(sizeof(T::__Serializer) == 1U);
			return new typename T::__Serializer();
		}

	public:
		template<typename T>
		static void __registerType(int id)
		{
			initializeStatics();
			assert(_deserializerCreators->find(id) == _deserializerCreators->end());
			assert(_serializerCreators->find(id) == _serializerCreators->end());

			_deserializerCreators->insert({ id, _createDeserializer<T> });
			_serializerCreators->insert({ id, _createSerializer<T> });
		}
	};

	template<typename Base>
	std::map<int, typename Serializable<Base>::DeserializerCreator>
		* Serializable<Base>::_deserializerCreators;

	template<typename Base>
	std::map<int, typename Serializable<Base>::SerializerCreator>
		* Serializable<Base>::_serializerCreators;

	template<typename Base>
	static Base* deserialize(std::istream& in)
	{
		Serializable<Base>::initializeStatics();
		int id;
		binaryRead(id, in);
		if (Serializable<Base>::_deserializerCreators->find(id) 
				== Serializable<Base>::_deserializerCreators->end())
			throw std::runtime_error("Deserializer not found");
		auto impl = Serializable<Base>::_deserializerCreators->at(id)();
		Base* retval = impl->__deserialize(in);
		delete impl;
		return retval;
	}

	template<typename Base>
	void serialize(Base& obj, std::ostream& out)
	{
		Serializable<Base>::initializeStatics();
		int id = obj.__serialId();
		if (Serializable<Base>::_serializerCreators->find(id) 
				== Serializable<Base>::_serializerCreators->end())
			throw std::runtime_error("Serializer not found");
		binaryWrite(id, out);
		auto impl = Serializable<Base>::_serializerCreators->at(id)();
		impl->__serialize(obj, out);
		delete impl;
	}

	template <typename T, int ID>
	class _SerializableRegisterer
	{
	public:
		static bool init()
		{
			//cout << "Initializing: " << typeid(T).name() << " = " << ID << endl;
			Serializable<typename T::__SerializableBase>::template __registerType<T>(ID);
			return true;
		}

		static bool __dummy_val;
	};

	template <typename T, int ID> bool _SerializableRegisterer<T, ID>::__dummy_val = 
		_SerializableRegisterer<T, ID>::init();

#define serializable_abstract \
	public:\
	class __Serializer : public __SerializerImplementation {\
		inline void __serialize(__SerializableBase& obj, std::ostream& out) override {}};\
	class __Deserializer : public __DeserializerImplementation {\
		inline __SerializableBase* __deserialize(std::istream& in) override {return nullptr;}};

#define serializable_id(type, id) \
	public:\
	bool __serialization_helper_dummy = anip::_SerializableRegisterer<type, id>::__dummy_val;\
	int __serialId() const override { return id; }\
	class __Serializer : public __SerializerImplementation {\
		void __serialize(__SerializableBase& obj, std::ostream& out) override;};\
	class __Deserializer : public __DeserializerImplementation {\
		__SerializableBase* __deserialize(std::istream& in) override;};

#define serializer_implementation(type) \
	void type::__Serializer::__serialize

#define deserializer_implementation(type) \
	type::__SerializableBase* type::__Deserializer::__deserialize
}